package com.javabi.common.concurrent.lock;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * A thread-safe, highly concurrent lock factory. Every key has its own unique lock. References to locks and keys are
 * never purged, so the number of keys must be known and strictly limited.
 */
public class ConcurrentLockFactory<K> implements ILockFactory<K> {

	private final boolean fair;
	private final ConcurrentMap<K, Lock> map;

	public ConcurrentLockFactory(boolean fair) {
		this.fair = fair;
		this.map = new ConcurrentHashMap<K, Lock>();
	}

	@Override
	public Lock getLock(K key) {
		if (key == null) {
			throw new NullPointerException("key");
		}

		// For high concurrency, using putIfAbsent()
		Lock lock = map.get(key);
		if (lock == null) {
			lock = new ReentrantLock(fair);
			Lock existingLock = map.putIfAbsent(key, lock);
			if (existingLock != null) {
				lock = existingLock;
			}
		}

		return lock;
	}

}
